SQLiteは軽量で高速ですが、設計を誤ると一気に遅くなるデータベースです。 特に C# × SQLite の業務アプリでは、 インデックス・WAL・大量INSERT・SELECT最適化 が速度を左右します。 この記事では、SQLiteを最大限高速化するための実務テクニックをまとめます。
この記事でわかること
・インデックス設計の正解
・WALモードで読み書き高速化
・VACUUM / ANALYZE の使いどころ
・大量INSERTを100倍速くする方法
・SELECT最適化の具体例
・C#での高速化テクニック
・業務アプリ向けベストプラクティス
・インデックス設計の正解
・WALモードで読み書き高速化
・VACUUM / ANALYZE の使いどころ
・大量INSERTを100倍速くする方法
・SELECT最適化の具体例
・C#での高速化テクニック
・業務アプリ向けベストプラクティス
1. インデックス設計が最重要(速度の8割を決める)
SQLiteの速度はインデックス設計でほぼ決まると言っても過言ではありません。
■ インデックスを貼るべき列
- WHERE句で頻繁に検索する列
- JOINに使う列
- ORDER BY に使う列
■ 例:Usersテーブルの検索高速化
CREATE INDEX idx_users_age ON Users(Age);
CREATE INDEX idx_users_name ON Users(Name);
■ NGパターン
- 全列にインデックス → 逆に遅くなる
- INSERT/UPDATEが多いのにインデックス貼りすぎ
2. WALモードで読み書き並列を高速化
SQLiteはデフォルトではロックしやすく、 読み書きが競合すると一気に遅くなります。 WAL(Write-Ahead Logging)モードにすると改善します。
■ WALモード有効化
PRAGMA journal_mode = WAL;
■ C#で設定
using var con = new SqliteConnection(_cs);
con.Open();
new SqliteCommand("PRAGMA journal_mode=WAL;", con).ExecuteNonQuery();
WALのメリット
- 読み込みと書き込みが同時に可能
- ロックが大幅に減る
- 業務アプリではほぼ必須
3. VACUUM / ANALYZE で最適化
■ VACUUM(断片化解消・サイズ縮小)
VACUUM;
DBが肥大化してきたら実行すると効果的。
■ ANALYZE(統計情報の更新)
ANALYZE;
インデックスの利用効率が改善され、SELECTが速くなります。
4. 大量INSERTを100倍速くする方法
SQLiteは1件ずつINSERTすると非常に遅いです。 高速化の鍵は次の3つ。
① トランザクションでまとめる
using var tran = con.BeginTransaction();
foreach (var u in users)
{
// INSERT
}
tran.Commit();
② Prepare(プリペアドステートメント)を使う
var cmd = con.CreateCommand();
cmd.CommandText = "INSERT INTO Users (Name, Age) VALUES (@name, @age)";
cmd.Prepare();
foreach (var u in users)
{
cmd.Parameters["@name"].Value = u.Name;
cmd.Parameters["@age"].Value = u.Age;
cmd.ExecuteNonQuery();
}
③ WALモードと組み合わせる
WAL+トランザクション+Prepare で100倍以上高速化するケースもあります。
5. SELECT最適化(遅いSELECTの原因は決まっている)
■ 遅いSELECTの典型原因
- インデックスが無い
- 不要な列をSELECTしている(SELECT *)
- LIKE '%xxx%' を多用
- ORDER BY が重い
■ 最適化例
① 必要な列だけ取得
SELECT Id, Name FROM Users WHERE Age >= 20;
② LIKE検索は前方一致にする
-- 遅い
WHERE Name LIKE '%tar%'
-- 速い(インデックス利用可能)
WHERE Name LIKE 'tar%'
③ LIMIT / OFFSET でページング
SELECT * FROM Users ORDER BY Id LIMIT 100 OFFSET 0;
6. C#での高速化テクニック
■ DataTable.Load は同期 → Task.Run で逃がす
await Task.Run(() => dt.Load(reader));
■ 非同期APIを使う
await cmd.ExecuteReaderAsync();
await cmd.ExecuteNonQueryAsync();
■ 接続は短く・必要なときだけ開く
長時間接続はロックの原因にもなる。
7. キャッシュ戦略(アプリ側で高速化)
SQLiteは軽いので、アプリ側キャッシュと相性が良いです。
- マスタデータは起動時に読み込んでキャッシュ
- 更新頻度の低いデータはメモリ保持
- 検索条件が同じならアプリ側キャッシュを返す
8. 業務アプリ向けベストプラクティス
- インデックス設計が最重要
- WALモードは必ずON
- 大量INSERTはトランザクション+Prepare
- SELECTは必要な列だけ取得
- LIKE '%xxx%' は避ける
- VACUUM / ANALYZE を定期実行
- 非同期処理でUIフリーズを防ぐ
まとめ:SQLiteは“設計次第で”爆速にも激遅にもなる
- インデックス・WAL・トランザクションが速度の三本柱
- 大量INSERTはまとめて一気に
- SELECTはインデックスを意識して書く
- アプリ側キャッシュも効果的
SQLiteは軽量で扱いやすい反面、 最適化しないと簡単に遅くなるDBです。 この記事のテクニックを組み合わせれば、 “爆速で壊れないSQLiteアプリ”を構築できます。